{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "<a target=\"_blank\" href=\"https://colab.research.google.com/github/opensim-org/opensim-core/blob/main/Bindings/Python/tutorials/Tutorial%201%20-%20Introduction%20to%20OpenSim.ipynb\">\n",
        "  <img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/>\n",
        "</a>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YS-0eq5Y6BjU"
      },
      "source": [
        "# 1. Introduction to OpenSim"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ZZClpIjbitD4"
      },
      "source": [
        "\n",
        "## 1.1. Objectives"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AKANMMa6iycq"
      },
      "source": [
        "**Introduction to OpenSim**\n",
        "\n",
        "[OpenSim](https://opensim.stanford.edu/) is an open-source software that allows users to develop, analyze, and visualize models of the musculoskeletal system, and to generate dynamic simulations of movement [[1]](https://doi.org/10.1109/TBME.2007.901024). OpenSim enables users to create custom studies, including investigating the effects of musculoskeletal geometry, joint kinematics, and muscle-tendon properties on the forces and joint moments that the muscles can produce. With OpenSim, our goal is to provide a framework that allows the biomechanics community to create, share, and extend a library of models and dynamic simulation tools to study and quantify human and animal movement.\n",
        "\n",
        "**Purpose**\n",
        "\n",
        "The purpose of this tutorial is to introduce users to the OpenSim [conda package](https://anaconda.org/opensim-org/opensim), that allows the use of the library in [Jupyter notebooks](https://jupyter.org/) and other Python environments. In this tutorial you will:\n",
        "\n",
        "*   Learn to set up OpenSim in a Jupyter notebook using [Google Colab](https://colab.research.google.com/?hl=en).\n",
        "*   Create a simple pendulum in OpenSim.\n",
        "*   Visualize the position of the pendulum using [matplotlib](https://matplotlib.org/).\n",
        "\n",
        "**Format**\n",
        "\n",
        "This tutorial guides you through the setup of the OpenSim conda package in Google Colab. Then, to test if OpenSim was installed correctly, you will create a simple pendulum, simulate it, and visualize its position. As you complete each section of the tutorial, feel free to explore the OpenSim conda package and modify the code blocks of the tutorial on your own.\n",
        "\n",
        "**Local Installation**\n",
        "\n",
        "You may also run this notebook using a locally installed OpenSim `conda` package. Visit [the OpenSim python scripting Confluence page](https://opensimconfluence.atlassian.net/wiki/spaces/OpenSim/pages/53085346/Scripting+in+Python) for more information on how to install OpenSim in a local Python environment. Then, you may skip to Section 1.3 below."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kPnGlZIT8oiM"
      },
      "source": [
        "## 1.2. Set up `conda` and OpenSim"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3kxYrs4Hi6Nl"
      },
      "source": [
        "In the following cell, you will use [condacolab](https://github.com/conda-incubator/condacolab) to install [Miniconda](https://docs.conda.io/en/latest/miniconda.html). If you want to install a different conda distribution (e.g., Anaconda, Mambaforge) check [condacolab's documentation](https://github.com/conda-incubator/condacolab#usage). "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "5-sX9L3f6Asq"
      },
      "outputs": [],
      "source": [
        "!pip install -q condacolab\n",
        "import condacolab\n",
        "condacolab.install()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LdmpfqC8Dt0x"
      },
      "source": [
        "Don't worry if after executing the previous cell you get an error saying that your session has failed. This happens because `condacolab` needs to restart the session for the changes to take effect. Therefore, you have to execute the previous cell before executing any other cell.\n",
        "\n",
        "Now, execute the following cell to install the OpenSim `conda` package."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "H9WE2h3fD61J"
      },
      "outputs": [],
      "source": [
        "!conda install opensim-org::opensim"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hj5Y1HCXEb_M"
      },
      "source": [
        "## 1.3. Create a pendulum model"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1W4hVOJvEmc5"
      },
      "source": [
        "In this section we will create a simple pendulum, simulate it using time-stepping integration, and plot the resulting states trajectory. First, run the following cell to import the OpenSim module. If successful, the current OpenSim version and build date should print to the console."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "H4vWQbmhEjVr"
      },
      "outputs": [],
      "source": [
        "import opensim as osim\n",
        "osim.GetVersionAndDate()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RlqyWYr3HfAD"
      },
      "source": [
        "Now, let's create a pendulum model. We will use `ModelFactory`, a utility class that allows you to construct common OpenSim `Model`s. Here, we will create a single-link pendulum using `ModelFactor::createPendulum()`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "uCT1o9zzF-Nn"
      },
      "outputs": [],
      "source": [
        "# Create a single-link pendulum.\n",
        "pendulum = osim.ModelFactory.createPendulum()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "The following image shows the structure of the pendulum. It consists of a body attached to the ground through a pin joint.\n",
        "\n",
        "![215195548-9c713166-5cac-4ac4-9a7a-728ce74ff074.svg]()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bMYcGGuaetwS"
      },
      "source": [
        "Before simulating a `Model`, you must initialize the system. Use the `initSystem()` method, which both builds the underlying computational system and constructs a `SimTK::State` containing default values for the `Model`'s coordinates (i.e., joint angles and speeds)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "horV86-lePLd"
      },
      "outputs": [],
      "source": [
        "# Initialize the system and obtain the initial state.\n",
        "state = pendulum.initSystem()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xU1CWpsffGHM"
      },
      "source": [
        "The `Manager` class is the primary tool for creating a time-stepping simulation with an OpenSim `Model`. It manages the integration of the `Model`s system and records a trajectory of states. Run the following cell to construct a `Manager` object, simulate the pendulum model, and export a `TimeSeriesTable` containing the states trajectory."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "QIpWVzXOfGbX"
      },
      "outputs": [],
      "source": [
        "# Create a new Manager object for the pendulum model.\n",
        "manager = osim.Manager(pendulum)\n",
        "\n",
        "# Initialize the Manager using the state we obtained from Model::initSystem(). This sets\n",
        "# the initial conditions for the simulation. The pendulum will start at rest positioned\n",
        "# 90 degrees from the vertical, and therefore will swing down under the influence of \n",
        "# gravity during the simulation.\n",
        "manager.initialize(state)\n",
        "\n",
        "# Call Manager's \"integrate()\" method to simulate the pendulum for 10 seconds.\n",
        "state = manager.integrate(10.0)\n",
        "\n",
        "# Obtain a table containing the states of the simulation.\n",
        "statesTable = manager.getStatesTable()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "KFYZ4O9XN4xH"
      },
      "source": [
        "Next, we will extract the times and joint angles from the `TimeSeriesTable` returned by the `Manager`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "FPACtscIGPaC"
      },
      "outputs": [],
      "source": [
        "# Extract time values for the x-axis.\n",
        "times = statesTable.getIndependentColumn()\n",
        "\n",
        "# Extract the joint angle of the pendulum over time.\n",
        "angles = statesTable.getDependentColumn('/jointset/j0/q0/value')\n",
        "\n",
        "# Print number of states\n",
        "print(f'Number of simulation states in the time interval [0, {state.getTime()}]: {angles.size()}')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "EeSYY1DtOLhd"
      },
      "source": [
        "The data from the simulation can now be plotted using, for example, `matplotlib`. The following cell plots the joint angle of the pendulum over time."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "NX4GrIpXGP1F"
      },
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "\n",
        "# Plot pendulum's joint angle over time.\n",
        "plt.title(\"Pendulum Simulation\")\n",
        "plt.plot(times, angles.to_numpy())\n",
        "plt.xlabel(\"time (s)\")\n",
        "plt.ylabel(\"angle (rad)\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "J5mhS9zNX9ff"
      },
      "source": [
        "## 1.4. Conclusion"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "_M04jPJLuiUz"
      },
      "source": [
        "In this tutorial you used [condacolab](https://github.com/conda-incubator/condacolab) to install [Miniconda](https://docs.conda.io/en/latest/miniconda.html) in [Google Colab](https://colab.research.google.com/?hl=en). Then, you  installed Conda [OpenSim](https://opensim.stanford.edu/) in the Miniconda environment, and you tested it by creating and simulating a simple pendulum. Finally, you plotted the positions of the pendulum using [matplotlib](https://matplotlib.org/)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lLWgd8Noun6Y"
      },
      "source": [
        "## 1.5. Useful Links\n",
        "\n",
        "\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "m8M3dlT5xQcD"
      },
      "source": [
        "> **OpenSim Website:** https://opensim.stanford.edu/\n",
        ">\n",
        "> **OpenSim Python Scripting:** https://opensimconfluence.atlassian.net/wiki/spaces/OpenSim/pages/53085346/Scripting+in+Python\n",
        ">\n",
        "> **OpenSim API Documentation:** https://simtk.org/api_docs/opensim/api_docs/\n",
        "> \n",
        "> **OpenSim Creator Website:** https://opensimcreator.com/\n",
        "> \n",
        "> **SimTK Website:** https://simtk.org/projects/opensim\n",
        "> \n",
        "> **Biomechanics of Movement Course Videos:** https://www.youtube.com/channel/UCDNGy0KKNLQ-ztcL5h2Z6zA"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rFR2Rf9tk_9A"
      },
      "source": [
        "## 1.6 Acknowledgments\n",
        "\n",
        "Thanks to [OpenSimColab](https://simtk.org/projects/opencolab) project [[2]](https://doi.org/10.1080/10255842.2022.2104607) for creating the first OpenSim conda package."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dxMjdukAnH2N"
      },
      "source": [
        "## 1.7. References\n",
        "\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "WX2A_iaYulV1"
      },
      "source": [
        "> [1] Delp, S. L., Anderson, F. C., Arnold, A. S., Loan, P., Habib, A., John, C. T., Guendelman, E., & Thelen, D. G. (2007). **OpenSim: open-source software to create and analyze dynamic simulations of movement.** *IEEE Transactions on Bio-Medical Engineering*, 54(11), 1940–1950. https://doi.org/10.1109/TBME.2007.901024\n",
        ">\n",
        "> [2] Mokhtarzadeh, H., Jiang, F., Zhao, S., & Malekipour, F. (2022). **OpenColab project: OpenSim in Google colaboratory to explore biomechanics on the web.** *Computer Methods in Biomechanics and Biomedical Engineering*, 1–9. https://doi.org/10.1080/10255842.2022.2104607"
      ]
    }
  ],
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "provenance": []
    },
    "gpuClass": "standard",
    "kernelspec": {
      "display_name": "opensim",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.11.10"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
